from typing import List

from sqlalchemy import and_, func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload

from app.bot.services.helpers import generate_proxy
from app.database.enums import ServerPrivacy, ServerType
from app.database.models import Category, Order, Proxy, Server
from app.database.models.m2ms import orders_proxies, proxies_users
from app.database.services.servers import get_server
from app.database.services.users import get_user


async def get_proxy(session: AsyncSession, proxy_id: int, joined: bool = True) -> Proxy:
    q = select(Proxy).where(Proxy.id == proxy_id)

    if joined:
        q = q.options(joinedload(Proxy.orders), joinedload(Proxy.server))

    res = await session.execute(q)

    return res.scalar()


async def get_all_proxies(session: AsyncSession) -> List[Proxy]:
    q = select(Proxy).order_by(Proxy.was_added.desc()).options(joinedload(Proxy.server))

    res = await session.execute(q)

    return res.scalars().all()


async def get_available_proxies(
    session: AsyncSession,
    proxy_type: str = None,
    proxy_privacy: str = None,
    country_id: int = None,
    provider_id: int = None,
    category: Category = None,
    site_id: int = None,
    limit: int = None,
    user_telegram_id: int = None,
) -> int | List[Proxy]:

    user = await get_user(session, user_telegram_id)

    q = (
        select(Proxy)
        .outerjoin(Proxy.server)
        .outerjoin(Proxy.users)
        .outerjoin(Proxy.orders)
        .filter(Proxy.status != "invalid")
        .filter(Server.proxy_type == proxy_type)
        .filter(Server.proxy_privacy == proxy_privacy)
        .filter(Server.country_id == country_id)
        .filter(Server.provider_id == provider_id)
    )

    if proxy_privacy == "private":
        q = q.filter(and_(~Proxy.users.any(), ~Proxy.orders.any()))

    elif proxy_privacy == "shared":
        q = (
            q.filter(Server.categories.contains(category))
            .filter(~Proxy.users.contains(user))
            .filter(
                or_(
                    Order.site_id != site_id,
                    ~Proxy.orders.any(),
                )
            )
            .group_by(Proxy.id)
            .having(
                and_(
                    select(func.count())
                    .select_from(orders_proxies)
                    .where(orders_proxies.c.proxy_id == Proxy.id)
                    .as_scalar()
                    < 3,
                    select(func.count())
                    .select_from(proxies_users)
                    .where(proxies_users.c.proxy_id == Proxy.id)
                    .as_scalar()
                    < 3,
                )
            )
        ).options(joinedload(Proxy.users), joinedload(Proxy.orders))

    # if proxy_type == ServerType.mobile and proxy_privacy == ServerPrivacy.shared:
    #
    #     available_proxies = [
    #         proxy
    #         for proxy in available_proxies
    #         if len(proxy.orders) < 3 and len(proxy.users) < 3
    #     ]

    if limit:
        q = q.limit(limit)

    res = await session.execute(q)

    available_proxies = res.scalars().unique().all()

    return available_proxies


async def update_authorization_type(
    session: AsyncSession,
    proxies: list[Proxy],
    authorization_type: str,
) -> None:
    for proxy in proxies:
        proxy.authorization_type = authorization_type
    await session.commit()


async def update_rotation_period(
    session: AsyncSession,
    proxies: list[Proxy],
    rotation_period: int,
    reload_type: str,
) -> None:
    for proxy in proxies:
        proxy.reload_period = rotation_period
        proxy.reload_type = reload_type

    await session.commit()


async def change_expired_proxy(session: AsyncSession, proxies: list[Proxy]) -> None:
    for proxy in proxies:
        server = await get_server(session, proxy.server_id)
        proxy = await get_proxy(session, proxy.id, joined=True)
        login, password, reload_link = await generate_proxy()

        if server.proxy_type == ServerType.mobile:
            if server.proxy_privacy == ServerPrivacy.private:
                proxy.login = login
                proxy.password = password
                proxy.reload_link = reload_link
                proxy.authorization_type = "by_password"
                proxy.reload_period = 5
            else:
                # proxy.reload_type = "by_link"
                # proxy.reload_period = 0
                proxy.authorization_type = None

        if len(proxy.orders) <= 1:
            # Если этот заказ единственный
            proxy.status = "available"

    await session.commit()
